﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using OpenWaves.Reflection;

namespace OpenWaves.Testing.CodeAnalysisRules
{
    public class AttributesShouldHaveAttributeUsageSpecified : ICodeAnalysisRule
    {
        private static readonly IEnumerable<Func<Type, bool>> exceptionRules = new Func<Type, bool>[]
                                      {
                                          t => t.IsAbstract,
                                          t => t.FullName.StartsWith("System.Diagnostics.Contracts."),
                                          t => t.FullName.StartsWith("JetBrains.")
                                      };

        public void Run(Assembly assembly)
        {
            var customAttributeTypes = assembly.GetTypes()
                .Where(t => t.Is<Attribute>())
                .Where(t => exceptionRules.All(p => p(t) == false));

            foreach (var customAttributeType in customAttributeTypes)
            {
                var attributeUsageAttribute = customAttributeType.GetCustomAttributes<AttributeUsageAttribute>().SingleOrDefault();

                Assert.IsNotNull(attributeUsageAttribute,
                    "Attribute class {0} is not decorated with AttributeUsageAttribute. Consider applying the attribute or adding exception rule.",
                    customAttributeType);

                Assert.That(attributeUsageAttribute.ValidOn, Is.Not.EqualTo(AttributeTargets.All),
                    "Attribute class {0} is declared with attribute usage targets set to AttributeTargets.All. Consider applying more specific targets or adding exception rule.",
                    customAttributeType);
            }            
        }
    }
}